"""
UI自动化服务
"""

import json
import yaml
import asyncio
import subprocess
import signal
import psutil
from typing import List, Optional, Dict, Any
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc
from ..models.ui_automation import (
    UiAutomationScript, UiAutomationExecution, UiTaskResult, UiStepResult, 
    UiAutomationBatch, UiAutomationStatus, ExecutionStatus
)
from ..models.requirement import Requirement
from ..models.user import User
from ..schemas.ui_automation import (
    UiAutomationScriptCreate, UiAutomationScriptUpdate, YamlGenerationRequest,
    UiAutomationExecutionCreate, UiAutomationBatchCreate
)
from .ai_service import AIService
from .ui_file_service import UiFileService


class UiAutomationService:
    """UI自动化服务类"""
    
    # 类级别的进程映射，用于跟踪正在执行的进程
    _execution_processes = {}

    def __init__(self, db: Session):
        self.db = db
        self.ai_service = AIService()
        self.ui_file_service = UiFileService()

    # 脚本管理相关方法
    async def create_script(self, script_data: UiAutomationScriptCreate, user_id: int) -> UiAutomationScript:
        """创建UI自动化脚本"""
        script = UiAutomationScript(
            **script_data.dict(),
            created_by=user_id
        )
        self.db.add(script)
        self.db.commit()
        self.db.refresh(script)
        
        # 保存到文件系统
        if script.yaml_content:
            try:
                self.ui_file_service.save_script_to_file(script)
            except Exception as e:
                print(f"保存脚本文件失败: {str(e)}")
        
        return script

    def get_scripts(
        self, 
        page: int = 1, 
        size: int = 20, 
        search: Optional[str] = None,
        requirement_id: Optional[int] = None,
        status: Optional[UiAutomationStatus] = None,
        tags: Optional[str] = None,
        created_by: Optional[int] = None
    ) -> Dict[str, Any]:
        """获取脚本列表"""
        query = self.db.query(UiAutomationScript)

        # 搜索过滤
        if search:
            query = query.filter(
                or_(
                    UiAutomationScript.name.contains(search),
                    UiAutomationScript.description.contains(search)
                )
            )

        # 需求过滤
        if requirement_id:
            query = query.filter(UiAutomationScript.requirement_id == requirement_id)

        # 状态过滤
        if status:
            query = query.filter(UiAutomationScript.status == status)

        # 标签过滤
        if tags:
            query = query.filter(UiAutomationScript.tags.contains(tags))

        # 创建人过滤
        if created_by:
            query = query.filter(UiAutomationScript.created_by == created_by)

        # 总数
        total = query.count()

        # 分页
        offset = (page - 1) * size
        scripts = query.order_by(desc(UiAutomationScript.created_at)).offset(offset).limit(size).all()

        # 计算总页数
        pages = (total + size - 1) // size

        return {
            "items": scripts,
            "total": total,
            "page": page,
            "size": size,
            "pages": pages
        }

    def get_script(self, script_id: int) -> Optional[UiAutomationScript]:
        """获取单个脚本"""
        return self.db.query(UiAutomationScript).filter(UiAutomationScript.id == script_id).first()

    async def update_script(self, script_id: int, script_data: UiAutomationScriptUpdate) -> Optional[UiAutomationScript]:
        """更新脚本"""
        script = self.get_script(script_id)
        if not script:
            return None

        # 记录是否更新了YAML内容
        yaml_updated = 'yaml_content' in script_data.dict(exclude_unset=True)

        for field, value in script_data.dict(exclude_unset=True).items():
            setattr(script, field, value)

        self.db.commit()
        self.db.refresh(script)
        
        # 如果更新了YAML内容，同步到文件系统
        if yaml_updated and script.yaml_content:
            try:
                self.ui_file_service.save_script_to_file(script)
            except Exception as e:
                print(f"更新脚本文件失败: {str(e)}")
        
        return script

    def delete_script(self, script_id: int) -> bool:
        """删除脚本"""
        script = self.get_script(script_id)
        if not script:
            return False

        # 删除文件系统中的脚本文件
        try:
            self.ui_file_service.delete_script_file(script_id)
        except Exception as e:
            print(f"删除脚本文件失败: {str(e)}")

        self.db.delete(script)
        self.db.commit()
        return True

    # YAML脚本生成相关方法
    async def generate_yaml_from_requirement(self, request: YamlGenerationRequest) -> Dict[str, str]:
        """基于需求生成YAML脚本"""
        # 构建AI提示词
        prompt = f"""
        基于以下需求内容生成UI自动化测试的YAML脚本。

        需求内容：
        {request.requirement_content}

        测试场景：
        {', '.join(request.test_scenarios)}

        额外要求：
        {request.additional_requirements or '无'}

        请生成符合以下格式的YAML脚本：
        tasks:
          - name: <任务名称>
            continueOnError: <boolean>
            flow:
              - ai: <操作描述>
              - aiTap: <点击元素描述>
              - aiInput: <输入内容>
                locate: <输入框描述>
              - aiAssert: <断言描述>
              - logScreenshot: <截图标题>

        生成的脚本应该：
        1. 覆盖所有测试场景
        2. 包含适当的断言
        3. 在关键步骤添加截图
        4. 使用自然语言描述元素定位
        5. 考虑错误处理

        只返回YAML内容，不需要其他说明。
        """

        try:
            # 调用AI服务生成YAML
            yaml_content = await self.ai_service.generate_response(prompt)
            
            # 验证YAML格式
            try:
                yaml.safe_load(yaml_content)
            except yaml.YAMLError:
                # 如果YAML格式有误，尝试修复
                yaml_content = self._fix_yaml_format(yaml_content)

            # 生成脚本名称和描述
            script_name = f"UI自动化测试_{request.requirement_id}"
            description = f"基于需求生成的UI自动化测试脚本"

            return {
                "yaml_content": yaml_content,
                "script_name": script_name,
                "description": description
            }

        except Exception as e:
            raise Exception(f"生成YAML脚本失败: {str(e)}")

    def _fix_yaml_format(self, yaml_content: str) -> str:
        """修复YAML格式"""
        # 简单的YAML格式修复逻辑
        try:
            # 如果包含代码块标记，移除它们
            if "```yaml" in yaml_content:
                yaml_content = yaml_content.replace("```yaml", "").replace("```", "")
            
            # 移除多余的空行
            lines = yaml_content.split('\n')
            cleaned_lines = [line for line in lines if line.strip()]
            
            return '\n'.join(cleaned_lines)
        except:
            # 如果修复失败，返回基本模板
            return """tasks:
  - name: "基础测试任务"
    continueOnError: false
    flow:
      - ai: "开始测试"
      - logScreenshot: "测试开始"
      - aiAssert: "页面加载完成"
"""

    def validate_yaml_script(self, yaml_content: str) -> Dict[str, Any]:
        """验证YAML脚本格式"""
        try:
            parsed_yaml = yaml.safe_load(yaml_content)
            
            # 检查必要的结构
            if not isinstance(parsed_yaml, dict):
                return {"valid": False, "error": "YAML内容必须是一个字典对象"}
            
            if "tasks" not in parsed_yaml:
                return {"valid": False, "error": "缺少'tasks'字段"}
            
            if not isinstance(parsed_yaml["tasks"], list):
                return {"valid": False, "error": "'tasks'必须是一个数组"}
            
            # 验证每个任务
            for i, task in enumerate(parsed_yaml["tasks"]):
                if not isinstance(task, dict):
                    return {"valid": False, "error": f"任务{i+1}必须是一个字典对象"}
                
                if "name" not in task:
                    return {"valid": False, "error": f"任务{i+1}缺少'name'字段"}
                
                if "flow" not in task:
                    return {"valid": False, "error": f"任务{i+1}缺少'flow'字段"}
                
                if not isinstance(task["flow"], list):
                    return {"valid": False, "error": f"任务{i+1}的'flow'必须是一个数组"}

            return {"valid": True, "parsed": parsed_yaml}
            
        except yaml.YAMLError as e:
            return {"valid": False, "error": f"YAML格式错误: {str(e)}"}

    # 执行相关方法
    async def create_execution(self, execution_data: UiAutomationExecutionCreate, user_id: int) -> UiAutomationExecution:
        """创建执行记录"""
        script = self.get_script(execution_data.script_id)
        if not script:
            raise ValueError("脚本不存在")

        execution = UiAutomationExecution(
            script_id=execution_data.script_id,
            execution_name=execution_data.execution_name or f"执行_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
            executed_by=user_id
        )
        self.db.add(execution)
        self.db.commit()
        self.db.refresh(execution)

        return execution

    async def create_execution_record(self, script_id: int, execution_options: Dict[str, Any] = None, user_id: int = None) -> UiAutomationExecution:
        """创建执行记录（不立即执行）"""
        # 获取脚本信息
        script = self.db.query(UiAutomationScript).filter(UiAutomationScript.id == script_id).first()
        script_name = script.name if script else f"脚本_{script_id}"
        
        # 先创建执行记录（临时execution_name）
        execution_data = UiAutomationExecutionCreate(
            script_id=script_id,
            execution_name="temp"
        )
        execution = await self.create_execution(execution_data, user_id)
        
        # 使用真正的execution_id生成执行文件名（这个名称将作为报告文件的前缀）
        execution_file_name = f"execution_{execution.id}"
        execution.execution_name = execution_file_name
        
        # 更新扩展信息
        execution.script_name = script_name
        execution.execution_type = 'single'  # 默认单个执行
        
        # 保存执行选项到执行记录的execution_options字段
        if execution_options:
            import json
            execution.execution_options = json.dumps(execution_options)
        
        self.db.commit()
        return execution

    async def execute_script_stream(self, execution_id: int, execution_options: Dict[str, Any] = None):
        """流式执行脚本，实时yield输出"""
        execution = self.db.query(UiAutomationExecution).filter(UiAutomationExecution.id == execution_id).first()
        if not execution:
            yield {"type": "error", "message": "执行记录不存在"}
            return

        script = execution.script
        if not script:
            yield {"type": "error", "message": "关联脚本不存在"}
            return

        # 从执行记录中读取执行选项
        if execution.execution_options:
            try:
                import json
                stored_options = json.loads(execution.execution_options)
                execution_options = stored_options if stored_options else {}
            except (json.JSONDecodeError, TypeError):
                execution_options = {}
        else:
            execution_options = execution_options or {}
                
        # 初始化执行日志
        execution_logs = []

        try:
            # 更新执行状态为运行中
            execution.status = ExecutionStatus.RUNNING
            execution.start_time = datetime.now()
            self.db.commit()

            yield {"type": "status", "message": "开始执行脚本", "status": "running"}

            # 创建临时文件
            temp_file = await self.ui_file_service.create_temp_script_file(
                script.yaml_content, 
                f"execution_{execution_id}"
            )

            yield {"type": "info", "message": f"创建临时脚本文件: {temp_file}"}

            # 流式执行Midscene
            async for output_chunk in self.ui_file_service.execute_midscene_script_stream(
                str(temp_file), 
                execution_options,
                execution_id
            ):
                # 收集执行日志
                if output_chunk.get('type') in ['stdout', 'stderr', 'command']:
                    log_entry = f"[{output_chunk.get('type', '')}] {output_chunk.get('message', '')}"
                    execution_logs.append(log_entry)
                
                yield output_chunk

            # 执行完成后的处理
            execution.status = ExecutionStatus.SUCCESS
            execution.end_time = datetime.now()
            execution.duration = int((execution.end_time - execution.start_time).total_seconds())
            
            # 保存执行日志
            if execution_logs:
                execution.execution_logs = '\n'.join(execution_logs)
            
            # 匹配并关联报告文件
            report_file = self.match_execution_with_report(execution_id)
            if report_file:
                execution.report_file_name = report_file
            
            # 读取并更新执行结果
            self.update_execution_result_from_json(execution_id, execution)
            
            self.db.commit()

            # 清理进程引用（使用类变量）
            UiAutomationService._execution_processes.pop(execution_id, None)

            yield {"type": "complete", "message": "脚本执行完成", "status": "success"}

        except Exception as e:
            # 执行失败的处理
            execution.status = ExecutionStatus.FAILED
            execution.end_time = datetime.now()
            execution.error_message = str(e)
            if execution.start_time:
                execution.duration = int((execution.end_time - execution.start_time).total_seconds())
            
            # 保存执行日志（即使失败也要保存）
            if execution_logs:
                execution.execution_logs = '\n'.join(execution_logs)
            
            # 即使执行失败，也尝试读取JSON结果（可能有部分执行结果）
            try:
                self.update_execution_result_from_json(execution_id, execution)
            except Exception as json_error:
                print(f"[Backend] 执行失败时读取JSON结果失败: {str(json_error)}")
            
            self.db.commit()

            # 清理进程引用（使用类变量）
            UiAutomationService._execution_processes.pop(execution_id, None)

            yield {"type": "error", "message": f"执行失败: {str(e)}", "status": "failed"}

    def match_execution_with_report(self, execution_id: int) -> Optional[str]:
        """匹配执行记录和报告文件"""
        try:
            execution = self.db.query(UiAutomationExecution).filter(UiAutomationExecution.id == execution_id).first()
            if not execution:
                return None
            
            # 查找匹配的报告文件
            report_dir = self.ui_file_service.ui_test_dir / "midscene_run" / "report"
            if not report_dir.exists():
                return None
                
            # 执行文件名作为前缀，查找匹配的HTML报告
            execution_prefix = f"execution_{execution_id}"
            
            for html_file in report_dir.glob(f"{execution_prefix}*.html"):
                # 更新执行记录的报告文件名（不在这里提交，由调用方决定）
                execution.report_file_name = html_file.name
                return html_file.name
                
            return None
            
        except Exception as e:
            print(f"匹配报告文件失败: {str(e)}")
            return None

    def update_execution_result_from_json(self, execution_id: int, execution: 'UiAutomationExecution'):
        """
        从JSON结果文件更新执行记录，然后删除JSON文件
        """
        try:
            # 获取执行名称
            execution_name = execution.execution_name or f"execution_{execution_id}"
            
            # 查找对应的JSON结果文件
            result_json = self.ui_file_service.find_execution_result_json(execution_name)
            if not result_json:
                print(f"[Backend] 未找到执行 {execution_name} 的结果文件")
                return
            
            # 解析结果数据
            parsed_result = self.ui_file_service.parse_execution_result(result_json, execution_name)
            if not parsed_result:
                print(f"[Backend] 解析执行结果失败或未找到匹配的脚本: {execution_name}")
                return
            
            # 双重验证：确保找到的结果确实属于当前执行
            if 'full_result_data' in parsed_result:
                results = parsed_result['full_result_data'].get('results', [])
                matched = False
                expected_filename = f"{execution_name}.yaml"
                
                for result in results:
                    script_path = result.get('script', '')
                    script_filename = script_path.split('/')[-1] if '/' in script_path else script_path
                    if script_filename == expected_filename:
                        matched = True
                        break
                
                if not matched:
                    print(f"[Backend] JSON结果验证失败，脚本名称不匹配: {execution_name}")
                    return
            
            # 更新执行记录
            if 'total_count' in parsed_result:
                execution.total_count = parsed_result['total_count']
            if 'success_count' in parsed_result:
                execution.success_count = parsed_result['success_count']
            if 'failed_count' in parsed_result:
                execution.failed_count = parsed_result['failed_count']
            if 'duration_seconds' in parsed_result:
                # 使用JSON中的更精确时长（如果可用）
                execution.duration = parsed_result['duration_seconds']
            if 'result_json_path' in parsed_result and parsed_result['result_json_path']:
                execution.result_json_path = parsed_result['result_json_path']
            if 'full_result_data' in parsed_result:
                import json
                execution.result_data = json.dumps(parsed_result['full_result_data'])
            
            # 根据JSON结果更新执行状态（只有当当前状态不是手动停止时才更新）
            if execution.error_message != "用户手动停止":
                if 'success' in parsed_result:
                    if parsed_result['success']:
                        execution.status = ExecutionStatus.SUCCESS
                    else:
                        execution.status = ExecutionStatus.FAILED
                        if not execution.error_message:  # 如果没有错误信息，从JSON结果推断
                            execution.error_message = "执行失败，详见结果数据"
            
            # 如果JSON中有报告文件信息且数据库中还没有，则更新
            if 'report_file' in parsed_result and parsed_result['report_file'] and not execution.report_file_name:
                report_filename = parsed_result['report_file'].split('/')[-1]  # 提取文件名
                execution.report_file_name = report_filename
            
            print(f"[Backend] 已更新执行记录 {execution_id} 的结果数据")
            
            # 删除JSON文件
            json_file_path = parsed_result.get('result_json_path')
            if json_file_path:
                try:
                    import os
                    if os.path.exists(json_file_path):
                        os.remove(json_file_path)
                        print(f"[Backend] 已删除JSON结果文件: {json_file_path}")
                    else:
                        print(f"[Backend] JSON文件不存在，无需删除: {json_file_path}")
                except Exception as delete_error:
                    print(f"[Backend] 删除JSON文件失败: {json_file_path}, 错误: {str(delete_error)}")
            
        except Exception as e:
            print(f"[Backend] 更新执行结果失败: {str(e)}")

    def get_executions_with_reports(
        self,
        page: int = 1,
        size: int = 20,
        script_id: Optional[int] = None,
        status: Optional[ExecutionStatus] = None,
        executed_by: Optional[int] = None
    ) -> Dict[str, Any]:
        """获取执行记录列表，包含报告信息"""
        executions_data = self.get_executions(page, size, script_id, status, executed_by)
        
        # 为每个执行记录匹配报告文件
        for execution in executions_data['items']:
            # execution是SQLAlchemy模型对象，不是字典
            if not execution.report_file_name:
                report_file = self.match_execution_with_report(execution.id)
                if report_file:
                    execution.report_file_name = report_file
                    self.db.commit()
                
        return executions_data

    async def stop_execution(self, execution_id: int) -> Dict[str, Any]:
        """停止执行中的脚本"""
        try:
            # 获取进程引用（使用类变量）
            process = UiAutomationService._execution_processes.get(execution_id)
            if not process:
                return {"success": False, "message": "进程不存在或已停止"}
            
            print(f"[Backend] 正在停止执行ID {execution_id}，PID: {process.pid}")
            
            # 尝试优雅停止
            try:
                process.terminate()
                # 等待进程终止，超时则强制杀死
                await asyncio.wait_for(process.wait(), timeout=5.0)
                print(f"[Backend] 进程已优雅停止")
            except asyncio.TimeoutError:
                print(f"[Backend] 优雅停止超时，强制杀死进程")
                process.kill()
                await process.wait()
            
            # 更新数据库状态
            execution = self.db.query(UiAutomationExecution).filter(
                UiAutomationExecution.id == execution_id
            ).first()
            
            if execution:
                execution.status = ExecutionStatus.FAILED
                execution.end_time = datetime.now()
                if execution.start_time:
                    execution.duration = int((execution.end_time - execution.start_time).total_seconds())
                execution.error_message = "用户手动停止"
                
                # 尝试读取JSON结果（即使被中断也可能有部分结果）
                try:
                    self.update_execution_result_from_json(execution_id, execution)
                except Exception as json_error:
                    print(f"[Backend] 手动停止时读取JSON结果失败: {str(json_error)}")
                
                self.db.commit()
            
            # 清理进程引用（使用类变量）
            UiAutomationService._execution_processes.pop(execution_id, None)
            
            return {"success": True, "message": "执行已停止", "execution_id": execution_id}
            
        except Exception as e:
            print(f"[Backend] 停止执行失败: {str(e)}")
            return {"success": False, "message": f"停止执行失败: {str(e)}"}

    async def execute_script_by_id(self, script_id: int, execution_options: Dict[str, Any] = None, user_id: int = None) -> Dict[str, Any]:
        """通过脚本ID直接执行脚本"""
        # 创建执行记录
        execution_data = UiAutomationExecutionCreate(
            script_id=script_id,
            execution_name=f"执行_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
        )
        execution = await self.create_execution(execution_data, user_id)
        
        # 执行脚本
        success, execution_details = await self.execute_script(execution.id, execution_options)
        
        return {
            "execution_id": execution.id,
            "success": success,
            "execution": execution,  # 返回SQLAlchemy对象，在API层转换
            "execution_details": execution_details  # 包含命令、输出等详细信息
        }

    async def execute_script(self, execution_id: int, options: Dict[str, Any] = None) -> tuple[bool, Dict[str, Any]]:
        """执行脚本"""
        execution = self.db.query(UiAutomationExecution).filter(UiAutomationExecution.id == execution_id).first()
        if not execution:
            return False, {"error": "执行记录不存在"}
        
        execution_details = {
            "script_name": execution.script.name,
            "execution_id": execution_id,
            "start_time": None,
            "command": None,
            "stdout": None,
            "stderr": None,
            "status": None
        }

        try:
            # 更新执行状态为运行中
            execution.status = ExecutionStatus.RUNNING
            execution.start_time = datetime.now()
            execution_details["start_time"] = execution.start_time.isoformat()
            execution_details["status"] = "RUNNING"
            self.db.commit()

            # 获取脚本内容
            script = execution.script
            yaml_content = script.yaml_content

            # 解析YAML内容
            validation_result = self.validate_yaml_script(yaml_content)
            if not validation_result["valid"]:
                raise Exception(f"YAML脚本格式错误: {validation_result['error']}")

            # 创建临时执行文件
            script_file = self.ui_file_service.create_execution_file(execution_id, yaml_content)

            # 使用Midscene执行脚本
            print(f"[UI自动化执行] 开始执行脚本: {script.name}")
            print(f"[UI自动化执行] 执行文件: {script_file}")
            print(f"[UI自动化执行] 执行选项: {options}")
            
            execution_result = await self.ui_file_service.execute_midscene_script(script_file, options)
            
            # 收集执行详情
            execution_details["command"] = execution_result.get("command", "")
            execution_details["stdout"] = execution_result.get("stdout", "")
            execution_details["stderr"] = execution_result.get("stderr", "")
            execution_details["returncode"] = execution_result.get("returncode", -1)
            
            # 打印执行命令和结果
            if "command" in execution_result:
                print(f"[UI自动化执行] 执行命令: {execution_result['command']}")
            
            if execution_result.get("stdout"):
                print(f"[UI自动化执行] 标准输出:")
                print(execution_result["stdout"])
            
            if execution_result.get("stderr"):
                print(f"[UI自动化执行] 错误输出:")
                print(execution_result["stderr"])
            
            print(f"[UI自动化执行] 执行结果: {'成功' if execution_result['success'] else '失败'}")
            print(f"[UI自动化执行] 返回码: {execution_result.get('returncode', 'N/A')}")

            # 更新执行记录
            if execution_result["success"]:
                execution.status = ExecutionStatus.SUCCESS
                execution.success_count = 1
                execution.failed_count = 0
            else:
                execution.status = ExecutionStatus.FAILED
                execution.success_count = 0
                execution.failed_count = 1
                execution.error_message = execution_result.get("stderr", "执行失败")

            execution.end_time = datetime.now()
            execution.duration = int((execution.end_time - execution.start_time).total_seconds())
            execution.total_count = 1

            self.db.commit()

            # 报告已经通过match_execution_with_report自动关联，不需要额外处理

            # 清理临时文件
            self.ui_file_service.cleanup_execution_file(execution_id)
            
            # 更新执行详情状态
            execution_details["status"] = "SUCCESS" if execution_result["success"] else "FAILED"

            return execution_result["success"], execution_details

        except Exception as e:
            # 更新执行状态为失败
            execution.status = ExecutionStatus.FAILED
            execution.end_time = datetime.now()
            if execution.start_time:
                execution.duration = int((execution.end_time - execution.start_time).total_seconds())
            execution.error_message = str(e)
            execution.total_count = 1
            execution.success_count = 0
            execution.failed_count = 1
            self.db.commit()

            # 清理临时文件
            try:
                self.ui_file_service.cleanup_execution_file(execution_id)
            except:
                pass
            
            # 更新执行详情
            execution_details["status"] = "FAILED"
            execution_details["stderr"] = f"执行异常: {str(e)}"

            return False, execution_details

    def get_executions(
        self,
        page: int = 1,
        size: int = 20,
        script_id: Optional[int] = None,
        status: Optional[ExecutionStatus] = None,
        executed_by: Optional[int] = None
    ) -> Dict[str, Any]:
        """获取执行记录列表"""
        query = self.db.query(UiAutomationExecution)

        if script_id:
            query = query.filter(UiAutomationExecution.script_id == script_id)

        if status:
            query = query.filter(UiAutomationExecution.status == status)

        if executed_by:
            query = query.filter(UiAutomationExecution.executed_by == executed_by)

        total = query.count()
        offset = (page - 1) * size
        executions = query.order_by(desc(UiAutomationExecution.created_at)).offset(offset).limit(size).all()

        pages = (total + size - 1) // size

        return {
            "items": executions,
            "total": total,
            "page": page,
            "size": size,
            "pages": pages
        }

    def get_execution(self, execution_id: int) -> Optional[UiAutomationExecution]:
        """获取执行记录详情"""
        return self.db.query(UiAutomationExecution).filter(UiAutomationExecution.id == execution_id).first()

    # 批量执行相关方法
    async def create_batch_execution(self, batch_data: UiAutomationBatchCreate, user_id: int) -> UiAutomationBatch:
        """创建批量执行"""
        batch = UiAutomationBatch(
            name=batch_data.name,
            description=batch_data.description,
            script_ids=json.dumps(batch_data.script_ids),
            total_scripts=len(batch_data.script_ids),
            created_by=user_id
        )
        self.db.add(batch)
        self.db.commit()
        self.db.refresh(batch)

        return batch

    async def execute_batch(self, batch_id: int) -> bool:
        """执行批量任务"""
        batch = self.db.query(UiAutomationBatch).filter(UiAutomationBatch.id == batch_id).first()
        if not batch:
            return False

        try:
            # 更新批量执行状态
            batch.status = ExecutionStatus.RUNNING
            batch.start_time = datetime.now()
            self.db.commit()

            script_ids = json.loads(batch.script_ids)
            success_count = 0
            failed_count = 0

            for script_id in script_ids:
                try:
                    # 为每个脚本创建执行记录
                    execution_data = UiAutomationExecutionCreate(
                        script_id=script_id,
                        execution_name=f"批量执行_{batch.name}_{script_id}"
                    )
                    execution = await self.create_execution(execution_data, batch.created_by)

                    # 执行脚本
                    result = await self.execute_script(execution.id)
                    if result:
                        success_count += 1
                    else:
                        failed_count += 1

                except Exception as e:
                    failed_count += 1

                # 更新批量执行进度
                batch.completed_scripts = success_count + failed_count
                batch.success_scripts = success_count
                batch.failed_scripts = failed_count
                self.db.commit()

            # 更新批量执行状态
            batch.status = ExecutionStatus.SUCCESS if failed_count == 0 else ExecutionStatus.FAILED
            batch.end_time = datetime.now()
            batch.duration = int((batch.end_time - batch.start_time).total_seconds())
            self.db.commit()

            return True

        except Exception as e:
            batch.status = ExecutionStatus.FAILED
            batch.end_time = datetime.now()
            if batch.start_time:
                batch.duration = int((batch.end_time - batch.start_time).total_seconds())
            self.db.commit()
            return False
